#![allow(unreachable_code)]
#![allow(unused_labels)]
#![allow(dead_code)]

fn if_else(){
    let n = 5;
    if n < 0 {
        println!("{} is negative", n);
    } else if n  > 0 {
        println!("{} is positive", n);
    } else {
        println!("{} is zero", n);
    }

    let big_n = 
        if n < 10 && n > -10 {
            println!(", and is a small number, increase then-fold");
            10 * n
        } else {
            println!(", and is a big number, halve the number");
            n / 2
        };
    println!("{} -> {}", n, big_n);
    
    
}



fn loop_(){
    let mut count = 0u32;

    println!("^^^^^##!!!Let's count until infinity!");

    loop {
        count += 1;
        if count == 3 {
            println!("three");
            continue;
        }

        println!("{}", count);

        if count == 5 {
            println!("OK, that's enough");
            break;
        }
    }
}
fn loop_nesting_labels(){
    'outer: loop {
        println!("Entered the outer loop");

        'inner: loop {
            println!("Entered the inner loop");


            break 'outer;
        }
        println!("This point will never be reached");
    }
    println!("Exited the outer loop");
}
fn return_from_loop(){
    let mut counter = 0;
    
    let result = loop {
        counter += 1;

        if counter == 10 {
            break counter * 2;
        }
    };

    assert_eq!(result, 20);
}



fn while_(){
    let mut n = 1;
    while n < 101 {
        if n % 15 == 0{
            println!("fizzbuzz");
        } else if n % 3 == 0 {
            println!("fizz");
        } else if n % 5 == 0 {
            println!("buzz");
        } else {
            println!("{}", n);
        }
        
        n += 1; 
    }
}

fn for_loop(){
    for n in 1..101 {
        if n % 15 == 0{
            println!("fizzbuzz");
        } else if n % 3 == 0 {
            println!("fizz");
        } else if n % 5 == 0 {
            println!("buzz");
        } else {
            println!("{}", n);
        }
    }


    for n in 1..=100 {
        if n % 15 == 0{
            println!("fizzbuzz");
        } else if n % 3 == 0 {
            println!("fizz");
        } else if n % 5 == 0 {
            println!("buzz");
        } else {
            println!("{}", n);
        }
    }
}

fn for_iter(){
    let names = vec!["Bob", "Frank", "Ferris"];

    for name in names.iter(){
        match name {
            &"Ferris" => println!("There is an rustacean among us"),
            _ => println!("Hello {}", name),
        }
    }
    println!("names:{:?}", names);
}

fn for_into_iter(){
    let names = vec!["Bob", "Frank", "Ferris"];

    for name in names.into_iter(){ //??? where moved ???
        match name {
            "Ferris" => println!("There is a runstacean among us"),
            _ => println!("Hello {}", name),
        }
    }
    //println!("names: {:?}", names);
}
fn for_iter_mut(){
    let mut names = vec!["Bob", "Frank", "Ferris"];

    for name in names.iter_mut(){
        *name = match name {
            &mut "Ferris" => "There is a rustacean among us!",
            _ => "Hello"
        }
    }
    println!("names: {:?}", names);
}

fn match_(){
    let number = 13;

    println!("Tell me about {}", number);

    match number {
        1 => println!("One"),
        2 | 3 | 5 | 7 | 11 => println!("This is a prime"),
        13..=19 => println!("A teen"),
        _ => println!("Ain't special"),
    }

    let boolean = true;
    let binary = match boolean {
        false => 0,
        true => 1,
    };
    println!("{} -> {}", boolean, binary);
}



enum Color {
    Red,
    Blue,
    Green,
    RGB(u32, u32, u32),
    HSV(u32, u32, u32),
    HSL(u32, u32, u32),
    CMY(u32, u32, u32),
    CMYK(u32, u32, u32, u32),
}
fn enums(){
    let color = Color::RGB(122, 17, 40);
    println!("what color is it?");

    match color {
        Color::Red => println!("The color is Red!"),
        Color::Blue => println!("The color is Blue!"),
        Color::Green => println!("The color is Green!"),

        Color::RGB(r, g, b) => println!("Red: {}, Green: {}, and Blue: {}!", r, g, b),
        Color::HSV(h, s, v) => println!("Hue: {}, saturation: {}, and value: {}!", h, s, v),
        Color::HSL(h, s, l) => println!("Hue: {}, saturation: {}, and lightness: {}!", h, s, l),
        Color::CMY(c, m, y) => println!("Cyan: {}, magenta: {}, and yellow: {}!", c, m, y),
        Color::CMYK(c, m, y, k) => println!("Cyan: {}, magenta: {}, yellow: {} and key: {}!", c, m, y, k),

    }
}

fn match_destructuring(){
    fn tuples(){
        let triple = (0 , -2, 3);

        println!("Tell me about {:?}", triple);

        match triple {
            (0, y, z) => println!("First is 0, y is {:?}, and z is {:?}", y, z),
            (1, ..) => println!("First is 1 and the rest doesn't matter"),
            _ => println!("It doesn't matter what they are"),
        }
    }
    tuples();
    
    fn arrays(){
        let array = [1, -2, 6];
        match array {
            [0, second, third] => 
                println!("array[0] = 0, array[1] = {}, array[2] = {}", second, third),
            [1, _, third ] => 
                println!("array[0] = 1, array[2] = {}", third),
            [-1, second, ..] => 
                println!("array[0] = -1, array[1] = {}", second),
            [3, second, tail @ ..] =>
                println!("array[0] = 3, array[1] = {}, and the other elements were {:?}", second, tail),
            [first, middle @.., last] => 
                println!("array[0] = {}, middle = {:?}, array[2] = {}", first, middle, last),
        }
    }
    arrays();

    enums();
    

    fn refs(){
        let reference = &4;
        match reference {
            &val => println!("Got a value via destructuring: {:?}", val),
        }
        match *reference {
            val => println!("Got a value via dereferencing: {:?}", val),
        }
        let _not_a_reference = 3;

        let ref _is_a_reference = 3; // ref is used in declaration

        let value = 5;
        match value {
            ref r => println!("Got a reference to a value: {:?}", r),
        }

        let mut mut_value = 6;
        match mut_value {
            ref mut m => {
                *m += 10;
                println!("We added 10. mut_value: {:?}", m);
            },
        }
    }
    refs();


    fn structs() {
        struct Foo {
            x: (u32, u32),
            y: u32,
        }

        let foo = Foo {x: (1, 2), y: 3};
        match foo {
            Foo {x: (1, b), y} => println!("First of x is 1, b = {}, y = {}", b, y),

            Foo {y:2, x: i} => println!("y is 2, i = {:?}", i),

            Foo {y, ..} => println!("y = {}, we don't care about x", y),

            // Compile Error: Foo {y } => println!("y = {}", y);
        }
    }
    structs();
}


fn match_guards(){
    let pair = (2, -2);

    println!("Tell me about {:?}", pair);

    match pair {
        (x, y) if x == y => println!("These are twins"),
        (x, y) if x + y == 0 => println!("Antimatter, boom"),
        (x, _) if x % 2 == 1 => println!("The first one is odd"),
        _ => println!("No correlation..."), // must be used in the end
    }

    let number : u8 = 4;
    match number {
        i if i == 0 => println!("Zero"),
        i if i > 0 => println!("Greater than zero"),
        _ => println!("Fell through"),
    }
}

fn match_bindings(){
    fn age() -> u32 { 15 }

    println!("Tell me what type of preson you are");

    match age(){
        0 => println!("I haven't celebrated my first birthday yet"),
        n @ 1 ..=12 => println!(" I'm a child of age {:?}", n),
        n @ 13 ..= 19 => println!(" I 'm a teen of age {:}", n),
        n => println!(" I'm an old person of age {:?}", n),
    }

    fn some_number() -> Option<u32> { Some(42) }

    match some_number() {
        Some(n @ 42) => println!("The Answer: {}!", n),
        Some(n) => println!("Not interesting ... {}", n),
        _ => (), //None
    }

}

fn if_let(){
    // case 1
    let optional = Some(7);
    match optional {
        Some(i) => { println!("This is a really long string and {:?}", i);},
        _ => {}, // required !!!
    }
    // ===>
    if let Some(i) = optional {
        println!("This is a really long string and {:?}", i);
    }

    // case 2
    let letter: Option<i32> = None;
    if let Some(i) = letter {
        println!("Matched {:?}", i);
    } else {
        println!("Didn't match a number. Let's go with a letter!");
    }

    // case 3
    let emoticon: Option<i32> = None;
    let i_like_letters = false;

    if let Some(i) = emoticon {
        println!("Match {:?}!", i);
    } else if i_like_letters {
        println!("Didn't match a number. Let go with a letter!");
    } else {
        println!("I don't like letters. Let go with an emoticon :)!");
    }

    //case 4 match any enum value 
    enum Foo {
        Bar, Baz, Qux(u32)
    }

    let a = Foo::Bar;
    if let Foo::Bar = a {
        println!("a is foobar");
    }

    let b = Foo::Baz;
    if let Foo::Bar = b {
        println!("b is foobar")
    }

    let c = Foo::Qux(100);
    if let Foo::Qux(value) = c {
        println!("c is {}", value);
    }

    // value is binding to 100
    if let Foo::Qux(_value @ 100) = c {
        println!("c is one hundred");
    }


    //this cause compile-time error, use if-let instead
    // if Foo::Bar == a { NOTE:
    if let Foo::Bar = a {
        println!("a is foobar");
    }
}



fn while_let(){
    let mut optional = Some(0);

    loop {
        match optional {
            Some(i) => {
                if i > 9 {
                    println!("Greater than 9 , quit");
                    optional = None;
                }
                else {
                    println!("i is {:?}. Try agian.", i);
                    optional = Some(i + 1);
                }
            },
            _ => {break;}
        }
    }

    optional = Some(0);
    while let Some(i) = optional {
        if i > 9 {
            println!("Greater than 9 , quit");
            optional = None;
        }
        else {
            println!("i is {:?}. Try agian.", i);
            optional = Some(i + 1);
        }
    }
}

pub fn main(){
    if_else();

    loop_();
    loop_nesting_labels();
    return_from_loop();

    while_();

    for_loop();
    for_iter();
    for_into_iter();
    for_iter_mut();

    match_();

    match_destructuring();

    match_guards();

    match_bindings();

    if_let();
    while_let();
}
